const std = @import("std");

fn tupleFieldIndex(comptime TupleType: type, comptime TargetFieldType: type) comptime_int {
    const ActualTargetFieldType = switch(@typeInfo(TargetFieldType)) {
        .Pointer => |p| p.child,
        else => TargetFieldType,
    };

    inline for (std.meta.fields(TupleType)) |field, i| {
        const FieldType = std.meta.Child(field.field_type);
        if (FieldType == ActualTargetFieldType) return i;
    }

    @compileError(@typeName(TupleType) ++ " does not have field of type " ++ @typeName(TargetFieldType));
}

pub fn Dispatcher(comptime systems: anytype, comptime resources: anytype) fn() anyerror!void {
    return struct {
        pub fn run() anyerror!void {
            const call_options = .{};
            inline for (systems) |system| {
                const SystemArgsTuple = std.meta.ArgsTuple(@TypeOf(system));
                var args: SystemArgsTuple = undefined;
                inline for (std.meta.fields(SystemArgsTuple)) |field, i| {
                    const j = tupleFieldIndex(@TypeOf(resources), field.field_type);
                    args[i] = resources[j];
                }
                try @call(call_options, system, args);
            }
        }
    }.run;
}